﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using caedisi.Web.Services;
using caedisi.Web.DataModel;
using System.ServiceModel.DomainServices.Client;
using System.Linq;
using caedisi.Helpers;
using caedisi.Models;
using System.Collections.ObjectModel;
using caedisi.Common;

namespace caedisi.Controller
{
    /// <summary>
    /// the controller object of the cellular automata
    /// </summary>
    public class InverterController
    {       
        private static object syncRoot = new Object();

        private CellularAutomataDomainContext _context;
        /// <summary>
        /// Gets the context.
        /// </summary>
        public CellularAutomataDomainContext Context
        {
            get { return _context; }
        }
       
        private static volatile InverterController instance;
        /// <summary>
        /// Gets the instance.
        /// </summary>
        public static InverterController Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                            instance = new InverterController();
                    }
                }
                return instance;
            }
        }
               

        /// <summary>
        /// Initializes a new instance of the <see cref="InverterController"/> class.
        /// </summary>
        public InverterController()
        {
            _context = new CellularAutomataDomainContext();
        }

        private CellularAutomata _CurrentEntity;
        /// <summary>
        /// Gets or sets the current entity.
        /// </summary>
        /// <value>
        /// The current entity.
        /// </value>
        public CellularAutomata CurrentEntity
        {
            get
            {
                return _CurrentEntity;
            }
            set
            {
                if (value == null)
                    _context.CellularAutomatas.Detach(_CurrentEntity);
                else
                {
                    ClearAllLoadedObjects();
                    _context.CellularAutomatas.Add(value);
                }
                _CurrentEntity = value;                
            }
        }

        #region Cellular Automata
        


        /// <summary>
        /// Removes the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Remove(CellularAutomata entity)
        {
            throw new NotImplementedException();
        }
        
        #endregion

        #region TransitionRule

        /// <summary>
        /// Removes the transition rule.
        /// </summary>
        /// <param name="transitionRule">The transition rule.</param>
        public void RemoveTransitionRule(TransitionRule transitionRule)
        {
            foreach (NeighbourhoodStateConfiguration neighbourStateConfiguration in transitionRule.NeighbourhoodStateConfigurations)
            {
                transitionRule.NeighbourhoodStateConfigurations.Remove(neighbourStateConfiguration);
                this.Context.NeighbourhoodStateConfigurations.Remove(neighbourStateConfiguration);
            }

            this.CurrentEntity.TransitionRules.Remove(transitionRule);
            this.Context.TransitionRules.Remove(transitionRule);
        }


        public  void ValidateAndStoreStates(InverterExecution currentInverterExecution, Collection<RuleValidationError> validationResultsCollisionRules)
        {
            if (currentInverterExecution.CellularAutomata.ImmunityTime == 0)
                ValidateAndStoreStatesNoImmunity(currentInverterExecution, validationResultsCollisionRules);
            else
                ValidateAndStoreStatesWithImmunity(currentInverterExecution, validationResultsCollisionRules);
        }

        private  void ValidateAndStoreStatesNoImmunity(InverterExecution currentInverterExecution, 
            Collection<RuleValidationError> validationResultsCollisionRules)
        {
            int currentStep = currentInverterExecution.CurrentStep;
            int previousStep = currentInverterExecution.CurrentStep - 1;

            int numberOfRows = currentInverterExecution.CellularAutomata.Height; ;
            int numberOfColumns = currentInverterExecution.CellularAutomata.Width;

            InvertedCellItem[, ,] previousCellularAutomataAsArrayInvertedCellItem = 
                currentInverterExecution.TemporalCellularAutomata[previousStep];

            //foreach (Cell cell in this.CurrentCellularAutomata.Cells)
            for (int i = 0; i < numberOfRows; i++)
            {
                for (int j = 0; j < numberOfColumns; j++)
                {
                    Cell myCell = currentInverterExecution.CellularAutomata.CellularAutomataAsArray[i, j, 0];
                    int currentCellStateTypeId = myCell.CellStateTypeId;

                    //get the previousCellStateTypeId
                    int previousCellStateTypeId = previousCellularAutomataAsArrayInvertedCellItem[i, j, 0].CellStateTypeId;

                    //build the transition Rule
                    TransitionRule newTransitionRule = EntityHelper.GenerateGenericTransitionRule(
                                "([" + i + "," + j + "]|" + previousStep + "->" + currentStep + ") ",
                                string.Empty,
                                currentInverterExecution.CellularAutomata.NeighbourhoodTypeId,
                                currentInverterExecution.CellularAutomata.RadiusVisibility);

                    newTransitionRule.CurrentCellStateTypeId = previousCellStateTypeId;
                    newTransitionRule.ResultCellStateTypeId = currentCellStateTypeId;
                    //even though it is system generated, it is still simulating a user created.
                    newTransitionRule.IsSystemGenerated = false;   
                    //now set the neighbourhood                        
                    foreach (NeighbourhoodStateConfiguration ruleStateConfiguration in newTransitionRule.NeighbourhoodStateConfigurations)
                    {
                        ruleStateConfiguration.CellStateTypeId = EntityHelper.GetNeighbourCellStateTypeId(
                            myCell,
                            previousCellularAutomataAsArrayInvertedCellItem,
                            ruleStateConfiguration,
                            currentInverterExecution.CellularAutomata.CircularVertical,
                            currentInverterExecution.CellularAutomata.CircularHorizontal,
                            currentInverterExecution.OutOfBoundCellStateTypeId);
                    }

                    //validate the new rule
                    bool hasInclusion = false;
                    bool hasCollision = false;
                    foreach (TransitionRule existingRule in currentInverterExecution.CellularAutomata.TransitionRules)
                    {
                        if (newTransitionRule.CurrentCellStateTypeId == existingRule.CurrentCellStateTypeId)
                        {
                            //inclusion
                            if (newTransitionRule.ResultCellStateTypeId == existingRule.ResultCellStateTypeId 
                                && EntityHelper.IsInputIsomorphic(newTransitionRule, existingRule))
                            {
                                hasInclusion = true;
                            }
                            //collision
                            if (newTransitionRule.ResultCellStateTypeId != existingRule.ResultCellStateTypeId 
                                && EntityHelper.IsInputIsomorphic(newTransitionRule, existingRule))
                            {
                                hasCollision = true;
                                newTransitionRule.CellularAutomata = currentInverterExecution.CellularAutomata;
                                validationResultsCollisionRules.Add(new RuleValidationError(newTransitionRule, existingRule));
                            }
                        }
                    }

                    if (!hasCollision && !hasInclusion)
                        currentInverterExecution.CellularAutomata.TransitionRules.Add(newTransitionRule);
                }

            }
        }

private  void ValidateAndStoreStatesWithImmunity(InverterExecution currentInverterExecution, 
                            Collection<RuleValidationError> validationResultsCollisionRules)
{
    int currentStep = currentInverterExecution.CurrentStep;
    int previousStep = currentInverterExecution.CurrentStep - 1;

    int numberOfRows = currentInverterExecution.CellularAutomata.Height; ;
    int numberOfColumns = currentInverterExecution.CellularAutomata.Width;

    InvertedCellItem[, ,] previousCellularAutomataAsArrayInvertedCellItem = 
        currentInverterExecution.TemporalCellularAutomata[previousStep];

    //foreach (Cell cell in this.CurrentCellularAutomata.Cells)
    for (int i = 0; i < numberOfRows; i++)
    {
        for (int j = 0; j < numberOfColumns; j++)
        {
            Cell myCell = currentInverterExecution.CellularAutomata.CellularAutomataAsArray[i, j, 0];
            int currentCellStateTypeId = myCell.CellStateTypeId;

            if (currentCellStateTypeId == ((int)EnumCellStateType.Contaminated))
            {
                ; //no need to perform as we explicity ignore immunity.
            }
            else
            {
                //get the previousCellStateTypeId
                int previousCellStateTypeId = previousCellularAutomataAsArrayInvertedCellItem[i, j, 0].CellStateTypeId;

                if (previousCellStateTypeId != currentCellStateTypeId)
                {
                    //build the transition Rule
                    TransitionRule newTransitionRule = EntityHelper.GenerateGenericTransitionRule(
                                "([" + i + "," + j + "]|" + previousStep + "->" + currentStep + ") ",
                                string.Empty,
                                currentInverterExecution.CellularAutomata.NeighbourhoodTypeId,
                                currentInverterExecution.CellularAutomata.RadiusVisibility);

                    newTransitionRule.CurrentCellStateTypeId = previousCellStateTypeId;
                    newTransitionRule.ResultCellStateTypeId = currentCellStateTypeId;
                    //even though it is system generated, it is still simulating a user created.
                    newTransitionRule.IsSystemGenerated = false;
                    //now set the neighbourhood                        
                    foreach (NeighbourhoodStateConfiguration ruleStateConfiguration in newTransitionRule.NeighbourhoodStateConfigurations)
                    {
                        ruleStateConfiguration.CellStateTypeId = EntityHelper.GetNeighbourCellStateTypeId(
                            myCell,
                            previousCellularAutomataAsArrayInvertedCellItem,
                            ruleStateConfiguration,
                            currentInverterExecution.CellularAutomata.CircularVertical,
                            currentInverterExecution.CellularAutomata.CircularHorizontal,
                            currentInverterExecution.OutOfBoundCellStateTypeId);
                    }
                    //validate the new rule
                    bool hasInclusion = false;
                    bool hasCollision = false;
                    foreach (TransitionRule existingRule in currentInverterExecution.CellularAutomata.TransitionRules)
                    {
                        if (newTransitionRule.CurrentCellStateTypeId == existingRule.CurrentCellStateTypeId)
                        {
                            //inclusion
                            if (newTransitionRule.ResultCellStateTypeId == existingRule.ResultCellStateTypeId
                                && EntityHelper.IsInputIsomorphic(newTransitionRule, existingRule))
                            {
                                hasInclusion = true;
                            }
                            //collision
                            if (newTransitionRule.ResultCellStateTypeId != existingRule.ResultCellStateTypeId
                                && EntityHelper.IsInputIsomorphic(newTransitionRule, existingRule))
                            {
                                if (existingRule.ResultCellStateTypeId == ((int)EnumCellStateType.Contaminated)
                                    && newTransitionRule.ResultCellStateTypeId == ((int)EnumCellStateType.Decontaminated))
                                {
                                    if (previousCellularAutomataAsArrayInvertedCellItem[i, j, 0].ImmunityTimerValue == 0)
                                    {
                                        hasCollision = true;
                                        newTransitionRule.CellularAutomata = currentInverterExecution.CellularAutomata;
                                        validationResultsCollisionRules.Add(new RuleValidationError(newTransitionRule, existingRule));
                                    }
                                    else
                                    {
                                        ; //no collision, protected by immunity
                                    }
                                }
                                else
                                {
                                    hasCollision = true;
                                    newTransitionRule.CellularAutomata = currentInverterExecution.CellularAutomata;
                                    validationResultsCollisionRules.Add(new RuleValidationError(newTransitionRule, existingRule));
                                }
                            }
                        }
                    }
                    if (!hasCollision && !hasInclusion)
                        currentInverterExecution.CellularAutomata.TransitionRules.Add(newTransitionRule);
                }
            }
        }
    }
}


        #endregion

        /// <summary>
        /// Saves the specified save completed.
        /// </summary>
        /// <param name="SaveCompleted">The save completed.</param>
        public void Save(Action<bool, Exception> SaveCompleted)
        {
            _context.SubmitChanges(
                  submitOperation =>
                  {
                      if (submitOperation.HasError)
                      {
                          ;
                      }
                      SaveCompleted(!submitOperation.HasError, submitOperation.Error);
                  },
                  null);
        }

        /// <summary>
        /// Gets a value indicating whether this instance has changes.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has changes; otherwise, <c>false</c>.
        /// </value>
        public bool HasChanges
        {
            get { return (_context != null ? _context.HasChanges : false); }
        }

        /// <summary>
        /// Rejects the changes.
        /// </summary>
        public void RejectChanges()
        {
            this._context.RejectChanges();
        }

        #region private methods

        /// <summary>
        /// Clears all loaded objects.
        /// </summary>
        private void ClearAllLoadedObjects()
        {
            _context.CellularAutomatas.Clear();
            _CurrentEntity = null;
        }

        #endregion


    }
}
